package com.starview.star.compiler.generate;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;

import com.starview.platform.data.IArray;
import com.starview.platform.data.IConstructor;
import com.starview.platform.data.IFunction;
import com.starview.platform.data.IMap;
import com.starview.platform.data.IPattern;
import com.starview.platform.data.IRecord;
import com.starview.platform.data.IRelation;
import com.starview.platform.data.IScalar;
import com.starview.platform.data.IValue;
import com.starview.platform.data.IValueVisitor;
import com.starview.platform.data.type.IType;
import com.starview.platform.data.type.Location;
import com.starview.platform.data.type.StandardTypes;
import com.starview.star.compiler.ErrorReport;
import com.starview.star.compiler.ast.Abstract;
import com.starview.star.compiler.ast.IAbstract;
import com.starview.star.compiler.cafe.CafeSyntax;

public class AbstractValue implements IValueVisitor
{
  private final Stack<IAbstract> stack;
  private final Location loc;

  public static IAbstract abstractValue(Location loc, IValue term, ErrorReport errors)
  {
    Stack<IAbstract> stack = new Stack<IAbstract>();
    AbstractValue visitor = new AbstractValue(loc, stack, errors);
    term.accept(visitor);
    assert stack.size() == 1;
    return stack.pop();
  }

  public static IAbstract abstractValue(Location loc, Object val, IType type)
  {
    if (type.equals(StandardTypes.charType))
      return Abstract.newChar(loc, (Integer) val);
    else if (val instanceof Integer)
      return Abstract.newInteger(loc, ((Integer) val));
    else if (val instanceof Double)
      return Abstract.newFloat(loc, ((Double) val));
    else if (val instanceof Character)
      return Abstract.newChar(loc, ((Character) val));
    else if (val instanceof String)
      return Abstract.newString(loc, ((String) val));
    else if (val instanceof Long)
      return Abstract.newLong(loc, (Long) val);
    else if (val instanceof Boolean)
      return Abstract.newBoolean(loc, (Boolean) val);
    else if (val instanceof BigDecimal)
      return Abstract.newBigdecimal(loc, (BigDecimal) val);
    else if (val instanceof IScalar<?>)
      return abstractValue(loc, ((IScalar<?>) val).getValue(), type);
    else
      throw new UnsupportedOperationException("not implemented");
  }

  private AbstractValue(Location loc, Stack<IAbstract> stack, ErrorReport errors)
  {
    this.stack = stack;
    this.loc = loc;
  }

  @Override
  public void visitScalar(IScalar<?> scalar)
  {
    Object val = scalar.getValue();
    stack.push(abstractValue(loc, val, scalar.getType()));
  }

  @Override
  public void visitRecord(IRecord agg)
  {
    throw new UnsupportedOperationException("not implemented");
  }

  @Override
  public void visitArray(IArray array)
  {
    throw new UnsupportedOperationException("not implemented");
  }

  @Override
  public void visitRelation(IRelation relation)
  {
    throw new UnsupportedOperationException("not implemented");
  }

  @Override
  public void visitFunction(IFunction fn)
  {
    throw new UnsupportedOperationException("not implemented");
  }

  @Override
  public void visitPattern(IPattern ptn)
  {
    throw new UnsupportedOperationException("not implemented");
  }

  @Override
  public void visitConstructor(IConstructor con)
  {
    List<IAbstract> args = new ArrayList<IAbstract>();
    for (int ix = 0; ix < con.size(); ix++) {
      con.getCell(ix).accept(this);
      args.add(stack.pop());
    }

    stack.push(CafeSyntax.constructor(loc, con.getLabel(), args));
  }

  @Override
  public void visitMap(IMap map)
  {
    throw new UnsupportedOperationException("not implemented");
  }
}
